package com.cardone.common.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.GeneralSecurityException;
import java.security.Key;
import java.security.SecureRandom;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;

import lombok.extern.slf4j.Slf4j;

import org.apache.commons.io.IOUtils;

@Slf4j
public class CipherUtils {
	/**
	 * 使用AES对文件进行加密和解密
	 *
	 */
	private static String type = "AES";

	/**
	 * 加密解密流
	 *
	 * @param in
	 *          加密解密前的流
	 * @param out
	 *          加密解密后的流
	 * @param cipher
	 *          加密解密
	 * @throws IOException
	 * @throws GeneralSecurityException
	 */
	private static void crypt(final InputStream in, final OutputStream out, final Cipher cipher) throws IOException, GeneralSecurityException {
		final int blockSize = cipher.getBlockSize() * 1000;
		final int outputSize = cipher.getOutputSize(blockSize);

		final byte[] inBytes = new byte[blockSize];
		byte[] outBytes = new byte[outputSize];

		int inLength = 0;

		while ((inLength = in.read(inBytes)) == blockSize) {
			final int outLength = cipher.update(inBytes, 0, blockSize, outBytes);
			out.write(outBytes, 0, outLength);
		}

		if (inLength > 0) {
			outBytes = cipher.doFinal(inBytes, 0, inLength);
		} else {
			outBytes = cipher.doFinal();
		}

		out.write(outBytes);
	}

	/**
	 * 把文件srcFile解密后存储为destFile
	 *
	 * @param srcFile
	 *          解密前的文件
	 * @param destFile
	 *          解密后的文件
	 * @param privateKey
	 *          密钥
	 * @throws GeneralSecurityException
	 * @throws IOException
	 */
	public static void decrypt(final String srcFile, final String destFile, final String privateKey) throws GeneralSecurityException, IOException {
		FileInputStream fis = null;
		FileOutputStream fos = null;
		try {
			final Key key = CipherUtils.getKey(privateKey);
			final Cipher cipher = Cipher.getInstance(CipherUtils.type + "/ECB/PKCS5Padding");
			cipher.init(Cipher.DECRYPT_MODE, key);

			fis = new FileInputStream(srcFile);
			fos = new FileOutputStream(CipherUtils.mkdirFiles(destFile));

			CipherUtils.crypt(fis, fos, cipher);
		} catch (final IOException e) {
			CipherUtils.log.error(e.getMessage(), e);
		} finally {
			IOUtils.closeQuietly(fis);
			IOUtils.closeQuietly(fos);
		}
	}

	/**
	 * 把文件srcFile加密后存储为destFile
	 *
	 * @param srcFile
	 *          加密前的文件
	 * @param destFile
	 *          加密后的文件
	 * @param privateKey
	 *          密钥
	 * @throws GeneralSecurityException
	 * @throws IOException
	 */
	public static void encrypt(final String srcFile, final String destFile, final String privateKey) throws GeneralSecurityException, IOException {
		final Key key = CipherUtils.getKey(privateKey);

		final Cipher cipher = Cipher.getInstance(CipherUtils.type + "/ECB/PKCS5Padding");

		cipher.init(Cipher.ENCRYPT_MODE, key);

		FileInputStream fis = null;
		FileOutputStream fos = null;
		try {
			fis = new FileInputStream(srcFile);
			fos = new FileOutputStream(CipherUtils.mkdirFiles(destFile));

			CipherUtils.crypt(fis, fos, cipher);
		} catch (final IOException e) {
			CipherUtils.log.error(e.getMessage(), e);
		} finally {
			IOUtils.closeQuietly(fis);
			IOUtils.closeQuietly(fos);
		}
	}

	/**
	 * 生成指定字符串的密钥
	 *
	 * @param secret
	 *          要生成密钥的字符串
	 * @return secretKey 生成后的密钥
	 * @throws GeneralSecurityException
	 */
	private static Key getKey(final String secret) throws GeneralSecurityException {
		final KeyGenerator kgen = KeyGenerator.getInstance(CipherUtils.type);
		final SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
		secureRandom.setSeed(secret.getBytes());
		kgen.init(128, secureRandom);
		return kgen.generateKey();
	}

	/**
	 * 根据filePath创建相应的目录
	 *
	 * @param filePath
	 *          要创建的文件路经
	 * @return file 文件
	 * @throws IOException
	 */
	private static File mkdirFiles(final String filePath) throws IOException {
		final File file = new File(filePath);

		if (!file.getParentFile().exists()) {
			file.getParentFile().mkdirs();
		}
		file.createNewFile();

		return file;
	}
}
